namespace NHCodeFirst.FluentMapper
{
    using System;
    using System.Linq.Expressions;
    using Enums;
    using HbmModel;
    using Helpers;

    public class ManyToOneMapper<TEntity, TRelation>
    {
        private readonly ManyToOne manyToOne;

        internal ManyToOneMapper(ManyToOne manyToOne)
        {
            this.manyToOne = manyToOne;
        }

        public ManyToOneMapper<TEntity, TRelation> AddKey<TProperty>(Expression<Func<TEntity,TProperty>> selector, Action<ColumnMapper> mapper = null)
        {
            var column = new Column {Name = new ExpressionHelper().GetPropertyName(selector)};
            if (mapper != null)
                mapper(new ColumnMapper(column));
            manyToOne.Columns.Add(column);
            return this;
        }

        public ManyToOneMapper<TEntity, TRelation> AddKey(string name, Action<ColumnMapper> mapper = null)
        {
            var column = new Column {Name = name};
            if (mapper != null)
                mapper(new ColumnMapper(column));
            manyToOne.Columns.Add(column);
            return this;
        }

        public ManyToOneMapper<TEntity, TRelation> Lazy(bool value = true)
        {
            manyToOne.Lazy = value.ToString().ToLower();
            return this;
        }

        public ManyToOneMapper<TEntity, TRelation> PropertyRef<TProperty>(Expression<Func<TRelation,TProperty>> selector)
        {
            manyToOne.PropertyRef = new ExpressionHelper().GetPropertyName(selector);
            return this;
        }

        public ManyToOneMapper<TEntity, TRelation> ForeignKey(string value)
        {
            manyToOne.ForeignKey = value;
            return this;
        }

        public ManyToOneMapper<TEntity, TRelation> Update(bool value)
        {
            manyToOne.Update = value.ToString().ToLower();
            return this;
        }

        public ManyToOneMapper<TEntity, TRelation> Insert(bool value)
        {
            manyToOne.Insert = value.ToString().ToLower();
            return this;
        }

        public ManyToOneMapper<TEntity, TRelation> Fetch(Fetch value)
        {
            manyToOne.Fetch = value.ToString().ToLower();
            return this;
        }

        public ManyToOneMapper<TEntity, TRelation> Cascade(OneCascade cascade)
        {
            switch (cascade)
            {
                case OneCascade.SaveUpdate:
                    manyToOne.Cascade = "save-udate";
                    break;
                default:
                    manyToOne.Cascade = cascade.ToString().ToLower();
                    break;
            }
            return this;
        }

        public ManyToOneMapper<TEntity, TRelation> EntityName(string value)
        {
            manyToOne.EntityName = value;
            return this;
        }

        public ManyToOneMapper<TEntity, TRelation> Formula(string value)
        {
            manyToOne.Formula = value;
            return this;
        }

        public ManyToOneMapper<TEntity, TRelation> NotFound(NotFound notFound)
        {
            manyToOne.NotFound = notFound.ToString().ToLower();
            return this;
        }

        public ManyToOneMapper<TEntity, TRelation> OptimisticLock(bool value)
        {
            manyToOne.OptimisticLock = value.ToString().ToLower();
            return this;
        }

        public ManyToOneMapper<TEntity, TRelation> OuterJoin(JoinStrategy value)
        {
            manyToOne.OuterJoin = value.ToString().ToLower();
            return this;
        }

        public ManyToOneMapper<TEntity, TRelation> Access(Access access, Naming? namingStrategy = null)
        {
            return Access(access.ToString().ToLower(), namingStrategy);
        }

        public ManyToOneMapper<TEntity, TRelation> Access<T>(Naming? namingStrategy = null)
        {
            return Access(typeof(T).FullName, namingStrategy);
        }

        public ManyToOneMapper<TEntity, TRelation> Access(Type type, Naming? namingStrategy = null)
        {
            return Access(type.FullName, namingStrategy);
        }

        private ManyToOneMapper<TEntity, TRelation> Access(string access, Naming? naming)
        {
            manyToOne.Access = access;
            if (naming.HasValue)
                manyToOne.Access += "." + EnumsToHbmString.GetAccessNamingStrategy(naming.Value);
            return this;
        }
    }
}